FastAPI OAuth2 প্রমাণীকরণ আয়ত্ত করুন! এই গাইডটি পাসওয়ার্ড ফ্লো, ইমপ্লিসিট ফ্লো, অথরাইজেশন কোড ফ্লো, টোকেন রিফ্রেশ এবং শক্তিশালী API তৈরির জন্য নিরাপত্তা সেরা অনুশীলনগুলি কভার করে।
FastAPI OAuth2 ইমপ্লিমেন্টেশন: একটি বিস্তৃত প্রমাণীকরণ ফ্লো গাইড
আজকের ডিজিটাল ল্যান্ডস্কেপে, আপনার API সুরক্ষিত করা অত্যন্ত গুরুত্বপূর্ণ। OAuth2 (ওপেন অথরাইজেশন) প্রতিনিধি অথরাইজেশনের জন্য শিল্প মান হয়ে উঠেছে, যা ব্যবহারকারীদের তাদের শংসাপত্রগুলি ভাগ না করে তাদের সংস্থানগুলিতে সীমিত অ্যাক্সেস দেওয়ার অনুমতি দেয়। FastAPI, একটি আধুনিক, উচ্চ-কার্যকারিতা সম্পন্ন পাইথন ওয়েব ফ্রেমওয়ার্ক, OAuth2 প্রমাণীকরণ বাস্তবায়ন করাকে খুব সহজ করে তোলে। এই বিস্তৃত গাইডটি আপনাকে বিভিন্ন OAuth2 ফ্লো-এর মাধ্যমে পরিচালিত করবে এবং আপনার API সুরক্ষিত এবং অ্যাক্সেসযোগ্য থাকে তা নিশ্চিত করে কীভাবে সেগুলিকে আপনার FastAPI অ্যাপ্লিকেশনে সংহত করতে হয় তা প্রদর্শন করবে।
OAuth2 ধারণা বোঝা
কোডে ডুব দেওয়ার আগে, আসুন মূল OAuth2 ধারণাগুলির একটি স্পষ্ট ধারণা প্রতিষ্ঠা করি:
- রিসোর্স ওনার: ব্যবহারকারী যিনি ডেটার মালিক এবং অ্যাক্সেস প্রদান করেন।
- ক্লায়েন্ট: রিসোর্স ওনারের ডেটাতে অ্যাক্সেসের জন্য অনুরোধকারী অ্যাপ্লিকেশন। এটি একটি ওয়েব অ্যাপ্লিকেশন, মোবাইল অ্যাপ্লিকেশন বা অন্য কোনও পরিষেবা হতে পারে।
- অথরাইজেশন সার্ভার: রিসোর্স ওনারকে প্রমাণীকরণ করে এবং ক্লায়েন্টকে অথরাইজেশন প্রদান করে।
- রিসোর্স সার্ভার: সুরক্ষিত সংস্থানগুলি হোস্ট করে এবং অ্যাক্সেস দেওয়ার আগে অ্যাক্সেস টোকেন যাচাই করে।
- অ্যাক্সেস টোকেন: রিসোর্স ওনার কর্তৃক ক্লায়েন্টকে প্রদত্ত অথরাইজেশনের প্রতিনিধিত্বকারী একটি শংসাপত্র।
- রিফ্রেশ টোকেন: রিসোর্স ওনারকে পুনরায় অথরাইজ করার প্রয়োজন ছাড়াই নতুন অ্যাক্সেস টোকেন পাওয়ার জন্য ব্যবহৃত একটি দীর্ঘ-জীবী শংসাপত্র।
- স্কোপ: ক্লায়েন্ট কর্তৃক অনুরোধ করা নির্দিষ্ট অনুমতিগুলি সংজ্ঞায়িত করুন।
OAuth2 ফ্লো: সঠিক পদ্ধতি নির্বাচন করা
OAuth2 বেশ কয়েকটি অথরাইজেশন ফ্লো সংজ্ঞায়িত করে, প্রতিটি বিভিন্ন পরিস্থিতির জন্য উপযুক্ত। এখানে সবচেয়ে সাধারণ ফ্লোগুলির একটি তালিকা এবং কখন সেগুলি ব্যবহার করতে হবে:
1. পাসওয়ার্ড (রিসোর্স ওনার পাসওয়ার্ড ক্র্যাডেন্সিয়ালস) ফ্লো
বর্ণনা: ক্লায়েন্ট সরাসরি রিসোর্স ওনারের ব্যবহারকারীর নাম এবং পাসওয়ার্ড সরবরাহ করে অথরাইজেশন সার্ভার থেকে অ্যাক্সেস টোকেন পায়। ব্যবহারের ক্ষেত্র: অত্যন্ত বিশ্বস্ত অ্যাপ্লিকেশন, যেমন প্রথম পক্ষের মোবাইল অ্যাপ্লিকেশন। অন্য ফ্লো সম্ভব না হলে এটি ব্যবহার করা উচিত। সুবিধা: বাস্তবায়ন করা সহজ। অসুবিধা: ক্লায়েন্টকে রিসোর্স ওনারের শংসাপত্রগুলি পরিচালনা করতে হয়, ক্লায়েন্ট আপোস করলে প্রকাশের ঝুঁকি বাড়ায়। অন্যান্য ফ্লো থেকে কম সুরক্ষিত। উদাহরণ: একটি কোম্পানির নিজস্ব মোবাইল অ্যাপ্লিকেশন তাদের অভ্যন্তরীণ API অ্যাক্সেস করছে।
FastAPI-তে বাস্তবায়ন:
প্রথমে, প্রয়োজনীয় প্যাকেজগুলি ইনস্টল করুন:
pip install fastapi uvicorn python-multipart passlib[bcrypt] python-jose[cryptography]
এখন, আসুন একটি মৌলিক উদাহরণ তৈরি করি:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
app = FastAPI()
# Replace with a strong, randomly generated secret key
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# Password hashing configuration
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Dummy user database (replace with a real database in production)
users = {
"johndoe": {
"username": "johndoe",
"hashed_password": pwd_context.hash("password123"),
"scopes": ["read", "write"]
}
}
# Function to verify password
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# Function to create access token
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# OAuth2 endpoint for token generation
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = users.get(form_data.username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
if not verify_password(form_data.password, user["hashed_password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"], "scopes": user["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
# Dependency to authenticate requests
async def get_current_user(token: str):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = users.get(username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Example protected endpoint
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
ব্যাখ্যা:
- নির্ভরতা: আমরা ব্যবহারকারীর নাম এবং পাসওয়ার্ড পরিচালনার জন্য `fastapi.security.OAuth2PasswordRequestForm` ব্যবহার করি।
- পাসওয়ার্ড হ্যাশিং: `passlib` পাসওয়ার্ডগুলিকে সুরক্ষিতভাবে হ্যাশ এবং যাচাই করার জন্য ব্যবহৃত হয়। কখনও প্লেইন টেক্সটে পাসওয়ার্ড সংরক্ষণ করবেন না!
- JWT জেনারেশন: JSON ওয়েব টোকেন (JWTs) তৈরি এবং যাচাই করার জন্য `python-jose` ব্যবহৃত হয়।
- `/token` এন্ডপয়েন্ট: এই এন্ডপয়েন্টটি লগইন প্রক্রিয়া পরিচালনা করে। এটি ব্যবহারকারীর নাম এবং পাসওয়ার্ড যাচাই করে, এবং বৈধ হলে, একটি অ্যাক্সেস টোকেন তৈরি করে।
- `get_current_user` নির্ভরতা: এই ফাংশনটি অ্যাক্সেস টোকেন যাচাই করে এবং ব্যবহারকারীকে পুনরুদ্ধার করে।
- `/users/me` এন্ডপয়েন্ট: এটি একটি সুরক্ষিত এন্ডপয়েন্ট যা অ্যাক্সেস করার জন্য একটি বৈধ অ্যাক্সেস টোকেন প্রয়োজন।
2. ইমপ্লিসিট ফ্লো
বর্ণনা: রিসোর্স ওনার প্রমাণীকরণের পরে ক্লায়েন্ট সরাসরি অথরাইজেশন সার্ভার থেকে অ্যাক্সেস টোকেন পায়। অ্যাক্সেস টোকেনটি URL ফ্রেগমেন্টে ফেরত দেওয়া হয়। ব্যবহারের ক্ষেত্র: সিঙ্গেল-পেজ অ্যাপ্লিকেশন (SPAs) এবং অন্যান্য ব্রাউজার-ভিত্তিক অ্যাপ্লিকেশন যেখানে ক্লায়েন্ট সিক্রেট সংরক্ষণ করা সম্ভব নয়। সুবিধা: ব্রাউজার-ভিত্তিক অ্যাপ্লিকেশনগুলির জন্য সহজ। অসুবিধা: অন্যান্য ফ্লো থেকে কম সুরক্ষিত কারণ অ্যাক্সেস টোকেনটি URL-এ প্রকাশিত হয়। কোনও রিফ্রেশ টোকেন জারি করা হয় না। উদাহরণ: একটি জাভাস্ক্রিপ্ট অ্যাপ্লিকেশন একটি সোশ্যাল মিডিয়া API অ্যাক্সেস করছে।
FastAPI-তে বাস্তবায়ন বিবেচ্য বিষয়:
যদিও FastAPI ইমপ্লিসিট ফ্লো-এর ফ্রন্টএন্ড দিকগুলি সরাসরি পরিচালনা করে না (কারণ এটি প্রাথমিকভাবে একটি ব্যাকএন্ড ফ্রেমওয়ার্ক), আপনি প্রমাণীকরণ ফ্লো পরিচালনা করতে React, Vue বা Angular-এর মতো একটি ফ্রন্টএন্ড ফ্রেমওয়ার্ক ব্যবহার করবেন। FastAPI প্রাথমিকভাবে রিসোর্স সার্ভার হিসাবে কাজ করবে।
সরলীকৃত ব্যাকএন্ড (FastAPI - রিসোর্স সার্ভার) উদাহরণ:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2AuthorizationCodeBearer
from jose import JWTError, jwt
app = FastAPI()
# Replace with a strong, randomly generated secret key
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
# Dummy user database (replace with a real database in production)
users = {
"johndoe": {
"username": "johndoe",
"scopes": ["read", "write"]
}
}
# OAuth2 scheme - using AuthorizationCodeBearer for token verification
oauth2_scheme = OAuth2AuthorizationCodeBearer(authorizationUrl="/auth", tokenUrl="/token") # These URLs are handled by the Authorization Server (not this FastAPI app).
# Dependency to authenticate requests
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = users.get(username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Example protected endpoint
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
FastAPI-এর সাথে ইমপ্লিসিট ফ্লো-এর জন্য মূল বিষয়গুলি:
- অথরাইজেশন সার্ভারের ভূমিকা: প্রকৃত অথরাইজেশন এবং টোকেন ইস্যু করা একটি পৃথক অথরাইজেশন সার্ভারে ঘটে। FastAPI রিসোর্স সার্ভার হিসাবে কাজ করে, টোকেন যাচাই করে।
- ফ্রন্টএন্ড হ্যান্ডলিং: ফ্রন্টএন্ড অ্যাপ্লিকেশন (যেমন, React, Vue) অথরাইজেশন সার্ভারে রিডাইরেক্ট, ইউজার লগইন এবং URL ফ্রেগমেন্ট থেকে অ্যাক্সেস টোকেন পুনরুদ্ধার পরিচালনা করে।
- সুরক্ষা বিবেচনা: URL-এ অ্যাক্সেস টোকেন প্রকাশের কারণে, HTTPS ব্যবহার করা এবং টোকেনের জীবনকাল কম রাখা গুরুত্বপূর্ণ। PKCE সহ অথরাইজেশন কোড ফ্লোর পক্ষে সম্ভব হলে ইমপ্লিসিট ফ্লো এড়ানো উচিত।
3. অথরাইজেশন কোড ফ্লো
বর্ণনা: ক্লায়েন্ট প্রথমে অথরাইজেশন সার্ভার থেকে একটি অথরাইজেশন কোড পায়, যা পরে অ্যাক্সেস টোকেনের জন্য বিনিময় করে। এই ফ্লোতে ক্লায়েন্ট থেকে অথরাইজেশন সার্ভারে এবং পিছনে একটি রিডাইরেক্ট জড়িত। ব্যবহারের ক্ষেত্র: ওয়েব অ্যাপ্লিকেশন এবং মোবাইল অ্যাপ্লিকেশন যেখানে ক্লায়েন্ট সিক্রেট নিরাপদে সংরক্ষণ করা যায়। সুবিধা: ইমপ্লিসিট ফ্লো থেকে বেশি সুরক্ষিত কারণ অ্যাক্সেস টোকেন সরাসরি ব্রাউজারে প্রকাশিত হয় না। অসুবিধা: ইমপ্লিসিট ফ্লো থেকে বাস্তবায়ন করা আরও জটিল। উদাহরণ: কোনও তৃতীয় পক্ষের অ্যাপ্লিকেশন কোনও ব্যবহারকারীর Google ড্রাইভ ডেটাতে অ্যাক্সেসের জন্য অনুরোধ করছে।
PKCE (প্রুফ কী ফর কোড এক্সচেঞ্জ) সহ অথরাইজেশন কোড ফ্লো:
PKCE হল অথরাইজেশন কোড ফ্লোর একটি এক্সটেনশন যা অথরাইজেশন কোড ইন্টারসেপশনের ঝুঁকি হ্রাস করে। এটি মোবাইল অ্যাপ্লিকেশন এবং SPA-এর জন্য অত্যন্ত প্রস্তাবিত, কারণ এটির জন্য ক্লায়েন্টকে কোনও সিক্রেট সংরক্ষণের প্রয়োজন হয় না।
FastAPI-তে বাস্তবায়ন বিবেচ্য বিষয়: ইমপ্লিসিট ফ্লোর অনুরূপ, FastAPI মূলত এই ফ্লোতে রিসোর্স সার্ভার হিসাবে কাজ করবে। একটি পৃথক অথরাইজেশন সার্ভার প্রমাণীকরণ এবং অথরাইজেশন কোড ইস্যু করার জন্য দায়ী।
সরলীকৃত ব্যাকএন্ড (FastAPI - রিসোর্স সার্ভার) উদাহরণ (ইমপ্লিসিট ফ্লোর অনুরূপ):
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2AuthorizationCodeBearer
from jose import JWTError, jwt
app = FastAPI()
# Replace with a strong, randomly generated secret key
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
# Dummy user database (replace with a real database in production)
users = {
"johndoe": {
"username": "johndoe",
"scopes": ["read", "write"]
}
}
# OAuth2 scheme - using AuthorizationCodeBearer for token verification
oauth2_scheme = OAuth2AuthorizationCodeBearer(authorizationUrl="/auth", tokenUrl="/token") # These URLs are handled by the Authorization Server.
# Dependency to authenticate requests
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = users.get(username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Example protected endpoint
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
FastAPI-এর সাথে PKCE সহ অথরাইজেশন কোড ফ্লোর জন্য মূল বিষয়গুলি:
- অথরাইজেশন সার্ভারের ভূমিকা: অথরাইজেশন সার্ভার অথরাইজেশন কোড তৈরি, PKCE কোড যাচাইকারী যাচাইকরণ এবং অ্যাক্সেস টোকেন ইস্যু করার বিষয়গুলি পরিচালনা করে।
- ফ্রন্টএন্ড হ্যান্ডলিং: ফ্রন্টএন্ড অ্যাপ্লিকেশন একটি কোড যাচাইকারী এবং কোড চ্যালেঞ্জ তৈরি করে, ব্যবহারকারীকে অথরাইজেশন সার্ভারে পুনঃনির্দেশিত করে, অথরাইজেশন কোড গ্রহণ করে এবং অ্যাক্সেস টোকেনের জন্য এটি বিনিময় করে।
- বৃদ্ধিযুক্ত সুরক্ষা: PKCE অথরাইজেশন কোড ইন্টারসেপশন আক্রমণ প্রতিরোধ করে, এটিকে SPA এবং মোবাইল অ্যাপ্লিকেশনগুলির জন্য উপযুক্ত করে তোলে।
- প্রস্তাবিত পদ্ধতি: PKCE সহ অথরাইজেশন কোড ফ্লো সাধারণত আধুনিক ওয়েব এবং মোবাইল অ্যাপ্লিকেশনগুলির জন্য সবচেয়ে সুরক্ষিত এবং প্রস্তাবিত ফ্লো।
4. ক্লায়েন্ট ক্র্যাডেন্সিয়ালস ফ্লো
বর্ণনা: ক্লায়েন্ট অ্যাক্সেস টোকেন পাওয়ার জন্য তার নিজস্ব শংসাপত্রগুলি (ক্লায়েন্ট আইডি এবং ক্লায়েন্ট সিক্রেট) ব্যবহার করে সরাসরি অথরাইজেশন সার্ভারের সাথে প্রমাণীকরণ করে। ব্যবহারের ক্ষেত্র: মেশিন-টু-মেশিন যোগাযোগ, যেমন ব্যাকএন্ড পরিষেবাগুলি একে অপরের সাথে অ্যাক্সেস করছে। সুবিধা: ব্যাকএন্ড পরিষেবাগুলির জন্য সহজ। অসুবিধা: ব্যবহারকারী প্রমাণীকরণের জন্য উপযুক্ত নয়। উদাহরণ: একটি ডেটা প্রসেসিং পরিষেবা একটি ডাটাবেস পরিষেবা অ্যাক্সেস করছে।
FastAPI-তে বাস্তবায়ন:
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from jose import JWTError, jwt
from datetime import datetime, timedelta
app = FastAPI()
# Replace with a strong, randomly generated secret key
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
# Dummy client database (replace with a real database in production)
clients = {
"client_id": {
"client_secret": "client_secret",
"scopes": ["read", "write"]
}
}
# Function to create access token
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# HTTP Basic Authentication scheme
security = HTTPBasic()
# Endpoint for token generation
@app.post("/token")
async def login(credentials: HTTPBasicCredentials = Depends(security)):
client = clients.get(credentials.username)
if not client:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect client ID or secret",
headers={"WWW-Authenticate": "Basic"},
)
if credentials.password != client["client_secret"]:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect client ID or secret",
headers={"WWW-Authenticate": "Basic"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": credentials.username, "scopes": client["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
# Dependency to authenticate requests
async def get_current_client(token: str):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
client_id: str = payload.get("sub")
if client_id is None:
raise credentials_exception
except JWTError:
raise credentials_exception
client = clients.get(client_id)
if client is None:
raise credentials_exception
return client
async def get_current_active_client(current_client = Depends(get_current_client)):
return current_client
# Example protected endpoint
@app.get("/data")
async def read_data(current_client = Depends(get_current_active_client)):
return {"message": "Data accessed by client: " + current_client["client_secret"]}
ব্যাখ্যা:
- HTTP বেসিক প্রমাণীকরণ: ক্লায়েন্টকে প্রমাণীকরণের জন্য আমরা `fastapi.security.HTTPBasic` ব্যবহার করি।
- `/token` এন্ডপয়েন্ট: এই এন্ডপয়েন্টটি ক্লায়েন্ট প্রমাণীকরণ পরিচালনা করে। এটি ক্লায়েন্ট আইডি এবং গোপন যাচাই করে এবং বৈধ হলে একটি অ্যাক্সেস টোকেন তৈরি করে।
- `get_current_client` নির্ভরতা: এই ফাংশনটি অ্যাক্সেস টোকেন যাচাই করে এবং ক্লায়েন্টকে পুনরুদ্ধার করে।
- `/data` এন্ডপয়েন্ট: এটি একটি সুরক্ষিত এন্ডপয়েন্ট যা অ্যাক্সেস করার জন্য একটি বৈধ অ্যাক্সেস টোকেন প্রয়োজন।
টোকেন রিফ্রেশ
আপোস করা টোকেনগুলির প্রভাব কমাতে অ্যাক্সেস টোকেনগুলির সাধারণত একটি স্বল্প জীবনকাল থাকে। রিফ্রেশ টোকেনগুলি দীর্ঘ-জীবী শংসাপত্র যা ব্যবহারকারীকে পুনরায় অনুমোদনের প্রয়োজন ছাড়াই নতুন অ্যাক্সেস টোকেন পেতে ব্যবহার করা যেতে পারে।
বাস্তবায়ন বিবেচ্য বিষয়:
- রিফ্রেশ টোকেন সংরক্ষণ করা: রিফ্রেশ টোকেনগুলি নিরাপদে সংরক্ষণ করা উচিত, বিশেষত একটি ডাটাবেসে এনক্রিপ্ট করা।
- রিফ্রেশ টোকেন এন্ডপয়েন্ট: রিফ্রেশ টোকেন অনুরোধগুলি পরিচালনা করার জন্য একটি ডেডিকেটেড এন্ডপয়েন্ট (যেমন, `/refresh_token`) তৈরি করুন।
- রিফ্রেশ টোকেন বাতিল করা: যদি তারা আপোস করা হয় বা আর প্রয়োজন না হয় তবে রিফ্রেশ টোকেনগুলি বাতিল করার জন্য একটি প্রক্রিয়া প্রয়োগ করুন।
উদাহরণ (পাসওয়ার্ড ফ্লো উদাহরণ প্রসারিত করা হচ্ছে):
from fastapi import Depends, FastAPI, HTTPException, status
from fastapi.security import OAuth2PasswordRequestForm
from jose import JWTError, jwt
from passlib.context import CryptContext
from datetime import datetime, timedelta
import secrets # For generating secure random strings
app = FastAPI()
# Replace with a strong, randomly generated secret key
SECRET_KEY = "YOUR_SECRET_KEY"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
REFRESH_TOKEN_EXPIRE_DAYS = 30 # Longer lifetime for refresh tokens
# Password hashing configuration
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
# Dummy user database (replace with a real database in production)
users = {
"johndoe": {
"username": "johndoe",
"hashed_password": pwd_context.hash("password123"),
"scopes": ["read", "write"],
"refresh_token": None # Store refresh token here
}
}
# Function to verify password (same as before)
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
# Function to create access token (same as before)
def create_access_token(data: dict, expires_delta: timedelta):
to_encode = data.copy()
expire = datetime.utcnow() + expires_delta
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# Function to create refresh token
def create_refresh_token():
return secrets.token_urlsafe(32) # Generate a secure random string
# OAuth2 endpoint for token generation
@app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
user = users.get(form_data.username)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
if not verify_password(form_data.password, user["hashed_password"]):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
# Create refresh token and store it (securely in a database in real-world)
refresh_token = create_refresh_token()
user["refresh_token"] = refresh_token # Store it in the user object for now (INSECURE for production)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"], "scopes": user["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer", "refresh_token": refresh_token}
# Endpoint for refreshing the access token
@app.post("/refresh_token")
async def refresh_access_token(refresh_token: str):
# Find user by refresh token (securely query the database)
user = next((user for user in users.values() if user["refresh_token"] == refresh_token), None)
if not user:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid refresh token",
headers={"WWW-Authenticate": "Bearer"},
)
# Create a new access token
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user["username"], "scopes": user["scopes"]},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
# Dependency to authenticate requests (same as before)
async def get_current_user(token: str):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
except JWTError:
raise credentials_exception
user = next((user for user in users.values() if user["username"] == username), None)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user = Depends(get_current_user)):
return current_user
# Example protected endpoint (same as before)
@app.get("/users/me")
async def read_users_me(current_user = Depends(get_current_active_user)):
return {"username": current_user["username"], "scopes": current_user["scopes"]}
গুরুত্বপূর্ণ সুরক্ষা নোট:
- রিফ্রেশ টোকেন সংরক্ষণ করা: উদাহরণটি মেমরিতে (অনিরাপদে) রিফ্রেশ টোকেন সংরক্ষণ করে। একটি উত্পাদন পরিবেশে, ডাটাবেসে রিফ্রেশ টোকেনগুলি নিরাপদে সংরক্ষণ করুন, পছন্দসইভাবে এনক্রিপ্ট করা।
- রিফ্রেশ টোকেন রোটেশন: রিফ্রেশ টোকেন রোটেশন বাস্তবায়নের কথা বিবেচনা করুন। রিফ্রেশ টোকেন ব্যবহার করার পরে, একটি নতুন রিফ্রেশ টোকেন তৈরি করুন এবং পুরানোটি বাতিল করুন। এটি আপোস করা রিফ্রেশ টোকেনগুলির প্রভাবকে সীমাবদ্ধ করে।
- নিরীক্ষণ: সন্দেহজনক কার্যকলাপ সনাক্ত করতে রিফ্রেশ টোকেন ব্যবহার নিরীক্ষণ করুন।
নিরাপত্তা সেরা অনুশীলন
OAuth2 বাস্তবায়ন করা কেবল প্রথম পদক্ষেপ। আপনার API এবং ব্যবহারকারীর ডেটা সুরক্ষার জন্য সুরক্ষা সেরা অনুশীলন মেনে চলা অত্যন্ত গুরুত্বপূর্ণ।
- HTTPS ব্যবহার করুন: ক্লায়েন্ট, অথরাইজেশন সার্ভার এবং রিসোর্স সার্ভারের মধ্যে যোগাযোগ এনক্রিপ্ট করতে সর্বদা HTTPS ব্যবহার করুন।
- ইনপুট যাচাই করুন: ইনজেকশন আক্রমণ প্রতিরোধ করতে সমস্ত ইনপুট ডেটা ভালভাবে যাচাই করুন।
- হার সীমিত করা: জোরপূর্বক আক্রমণ প্রতিরোধ করতে হার সীমিতকরণ প্রয়োগ করুন।
- নিয়মিত নির্ভরতা আপডেট করুন: সুরক্ষা দুর্বলতাগুলি প্যাচ করতে আপনার FastAPI ফ্রেমওয়ার্ক এবং সমস্ত নির্ভরতা আপ-টু-ডেট রাখুন।
- শক্তিশালী গোপনীয়তা ব্যবহার করুন: আপনার ক্লায়েন্ট সিক্রেটস এবং JWT স্বাক্ষরকারী কীগুলির জন্য শক্তিশালী, র্যান্ডম সিক্রেটস তৈরি করুন। এই সিক্রেটসগুলি নিরাপদে সংরক্ষণ করুন (যেমন, পরিবেশের ভেরিয়েবল বা একটি সিক্রেটস ম্যানেজমেন্ট সিস্টেম ব্যবহার করে)।
- পর্যবেক্ষণ এবং লগ: সন্দেহজনক কার্যকলাপের জন্য আপনার API পর্যবেক্ষণ করুন এবং সমস্ত প্রমাণীকরণ এবং অনুমোদন ইভেন্ট লগ করুন।
- কম সুযোগ সুবিধা প্রয়োগ করুন: ক্লায়েন্টদের কেবলমাত্র প্রয়োজনীয় অনুমতিগুলি (স্কোপ) প্রদান করুন।
- সঠিক ত্রুটি পরিচালনা: ত্রুটি বার্তাগুলিতে সংবেদনশীল তথ্য প্রকাশ করা এড়িয়ে চলুন।
- একটি ভাল ভেটেড OAuth2 লাইব্রেরি ব্যবহার করার কথা বিবেচনা করুন: স্ক্র্যাচ থেকে OAuth2 বাস্তবায়নের পরিবর্তে, Authlib-এর মতো একটি ভাল ভেটেড লাইব্রেরি ব্যবহার করার কথা বিবেচনা করুন। Authlib OAuth2-এর একটি আরও শক্তিশালী এবং সুরক্ষিত বাস্তবায়ন প্রদান করে।
বেসিকের বাইরে: উন্নত বিবেচনা
একবার আপনার জায়গায় একটি বেসিক OAuth2 বাস্তবায়ন হয়ে গেলে, এই উন্নত বিষয়গুলি বিবেচনা করুন:
- সম্মতি পরিচালনা: ব্যবহারকারীদের ক্লায়েন্টদের দেওয়া অনুমতিগুলির উপর স্পষ্ট এবং দানাদার নিয়ন্ত্রণ সরবরাহ করুন।
- প্রতিনিধিত্বমূলক অনুমোদন: প্রতিনিধিত্বমূলক অনুমোদনের জন্য সমর্থন বাস্তবায়ন করুন, ব্যবহারকারীদের তাদের পক্ষ থেকে কাজ করার জন্য ক্লায়েন্টদের অনুমোদন করার অনুমতি দিন।
- মাল্টি-ফ্যাক্টর প্রমাণীকরণ (MFA): সুরক্ষা বাড়ানোর জন্য MFA সংহত করুন।
- ফেডারেটেড পরিচয়: তৃতীয় পক্ষের পরিচয় সরবরাহকারীদের (যেমন, Google, Facebook, Twitter) মাধ্যমে প্রমাণীকরণ সমর্থন করুন।
- ডায়নামিক ক্লায়েন্ট নিবন্ধন: ক্লায়েন্টদের আপনার অনুমোদন সার্ভারের সাথে গতিশীলভাবে নিবন্ধন করার অনুমতি দিন।
উপসংহার
FastAPI এর সাথে OAuth2 প্রমাণীকরণ বাস্তবায়ন করা আপনার API গুলি সুরক্ষিত করার এবং ব্যবহারকারীর ডেটা সুরক্ষার একটি শক্তিশালী উপায়। বিভিন্ন OAuth2 ফ্লো বোঝা, সুরক্ষা সেরা অনুশীলনগুলি বাস্তবায়ন করা এবং উন্নত বিষয়গুলি বিবেচনা করে আপনি আপনার ব্যবহারকারী এবং অ্যাপ্লিকেশনগুলির চাহিদা পূরণ করে এমন শক্তিশালী এবং সুরক্ষিত API তৈরি করতে পারেন। আপনার নির্দিষ্ট ব্যবহারের ক্ষেত্রে উপযুক্ত ফ্লো চয়ন করতে, সুরক্ষাকে অগ্রাধিকার দিতে এবং ক্রমাগত আপনার প্রমাণীকরণ সিস্টেম পর্যবেক্ষণ এবং উন্নত করতে মনে রাখবেন। যদিও প্রদত্ত উদাহরণগুলি মৌলিক নীতিগুলি প্রদর্শন করে, সর্বদা আপনার নির্দিষ্ট প্রয়োজনীয়তার সাথে তাদের খাপ খাইয়ে নিন এবং একটি সম্পূর্ণ পর্যালোচনার জন্য সুরক্ষা বিশেষজ্ঞদের সাথে পরামর্শ করুন।